#include "piocoreimpl.h"
#include "mainwindow.h"
#include "importmodule.h"
#include "importdialog.h"
#include "mediaitemimpl.h"
#include <QDir>
#include <QUrl>
#include <algorithm>
#include <windows.h>

PioCoreImpl::PioCoreImpl(MainWindow * mainwindow)
    : m_mainwindow(mainwindow),
      m_filters(*this),
      m_settings(QDir::homePath() + "/.pio/config.ini", QSettings::IniFormat)
{
#ifdef WIN32
    m_rootpath = QDir::homePath() + "/Pictures";
#else
    m_rootpath = QDir::homePath() + "/Bilder";
#endif
    m_rootpath = m_settings.value("imagefolder", m_rootpath).toString();
    QDir dir(m_rootpath);
    m_rootpath = dir.canonicalPath();

    connect(&m_database, SIGNAL(added(MediaItemPtr)), &m_filters, SLOT(onAdded(MediaItemPtr)));
    connect(&m_database, SIGNAL(removed(MediaItemPtr)), &m_filters, SLOT(onRemoved(MediaItemPtr)));
    connect(&m_database, SIGNAL(updated(MediaItemPtr)), &m_filters, SLOT(onUpdated(MediaItemPtr)));
    connect(&m_database, SIGNAL(added(MediaItemPtr)), this, SLOT(onAdded(MediaItemPtr)));

    connect(&m_filters, SIGNAL(added(MediaItemPtr)), this, SIGNAL(shown(MediaItemPtr)));
    connect(&m_filters, SIGNAL(removed(MediaItemPtr)), this, SIGNAL(hidden(MediaItemPtr)));
    connect(&m_filters, SIGNAL(updated(MediaItemPtr)), this, SIGNAL(updated(MediaItemPtr)));
    connect(&m_filters, SIGNAL(removed(MediaItemPtr)), this, SLOT(onItemGetInvisible(MediaItemPtr)));

    //connect(&m_library, SIGNAL(status(const QString &)), this, SLOT(setStatus(QString)));
    connect(&m_cache, SIGNAL(loaded(MediaItemPtr,QSize,QSharedPointer<QPixmap>)),
            this, SLOT(onLoaded(MediaItemPtr,QSize,QSharedPointer<QPixmap>)), Qt::QueuedConnection);
}

PioCoreImpl::~PioCoreImpl()
{

}

QMainWindow * PioCoreImpl::mainwindow()
{
    return m_mainwindow;
}

QToolBar * PioCoreImpl::toolbar()
{
    return m_mainwindow->toolbar();
}

QList<MediaItemPtr> PioCoreImpl::all()
{
    return m_database.all();
    //return m_library.items();
}

QList<MediaItemPtr> PioCoreImpl::view()
{
    QList<MediaItemPtr> tmp = m_filters.view().toList();
    qSort(tmp.begin(), tmp.end(), newer);
    return tmp;
}

template <typename T>
QList<T> reversed( const QList<T> & in ) {
    QList<T> result;
    result.reserve( in.size() ); // reserve is new in Qt 4.7
    std::reverse_copy( in.begin(), in.end(), std::back_inserter( result ) );
    return result;
}

QList<MediaItemPtr> PioCoreImpl::selection()
{
    QSet<MediaItemPtr> v = m_filters.view();
    QList<MediaItemPtr> r;
    foreach(MediaItemPtr item, v) {
        if(item->isSelected())
            r.append(item);
    }
    qSort(r.begin(), r.end(), newer);

    return reversed(r);
}

QList<MediaItemPtr> PioCoreImpl::checked()
{
    QList<MediaItemPtr> items = m_database.all();
    QList<MediaItemPtr> r;
    foreach(MediaItemPtr item, items) {
        if(item->isChecked())
            r.append(item);
    }
    qSort(r.begin(), r.end(), newer);
    return reversed(r);
}

QSet<MediaItemPtr> PioCoreImpl::set(MediaItemPtr item)
{
    return m_database.group(item);
}

bool PioCoreImpl::registerImportModule(ImportModule *im)
{
    if(m_importModules.contains(im->scheme()))
        return false;
    m_importModules[im->scheme()] = im;
    return true;
}

QString PioCoreImpl::libraryPath()
{
    return m_rootpath;
}

void PioCoreImpl::add(const QString & filename)
{
    MediaItemImpl::Metadata metadata;
    metadata.filename = filename;
    metadata.collection = Database::collection(m_rootpath, filename);
    Scanner::update(metadata);
    m_database.add(MediaItemPtr(new MediaItemImpl(metadata)));
}

bool PioCoreImpl::trash(MediaItemPtr item)
{
#ifdef __linux__
    if(QProcess::execute("trash-put", QStringList() << item->metadata().filename) != 0)
        return false;
#elif defined(_WIN32)
    WCHAR szFile[MAX_PATH+2];
    SHFILEOPSTRUCT op;
    lstrcpyn(szFile, item->metadata().filename.toStdWString().c_str(), sizeof(szFile));
    szFile[lstrlen(szFile)+1] = '\0'; // Extra terminating NUL req'd
    ZeroMemory(&op, sizeof(op));
    op.wFunc = FO_DELETE;
    op.pFrom = szFile;
    op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; // Options set for no user interaction
    SHFileOperation(&op);
#else
#error move to trash not implemented for your platform
    //    QFile::remove(item->metadata().filename);
#endif
    m_database.remove(item);
    return true;
}

bool PioCoreImpl::rename(MediaItemPtr item, const QString & filename)
{
    if(QFile::exists(filename) || !QFile::exists(item->metadata().filename))
        return false;

    QFileInfo fi(filename);
    QDir dir;
    dir.mkpath(fi.absolutePath());

    if(!QFile::rename(item->metadata().filename, filename))
        return false;

    MediaItemImpl::Metadata metadata = item->impl()->metadata();
    metadata.filename = filename;
    metadata.collection = Database::collection(m_rootpath, filename);
    m_database.update(item, metadata);
    return true;
}

QSettings & PioCoreImpl::settings()
{
    return m_settings;
}

QPixmap PioCoreImpl::icon(const MediaItemPtr & item, const QSize & size, bool sync)
{
    QSharedPointer<QPixmap> pm = item->impl()->m_icon;
    if(!pm.isNull()
            && ((pm->width() == size.width() && pm->height() <= size.height())
                || (pm->height() == size.height() && pm->width() <= size.width())))
        return *pm;
    if(sync) {
        QSharedPointer<QPixmap> pm = m_cache.load(item, size);
        return *pm;
    }
    m_cache.enqueue(item, size);
    return QPixmap();
}

QImage PioCoreImpl::load(const QString &filename, const QSize &size)
{
    ImageLoader loader;
    return loader.load(filename, size);
}

void PioCoreImpl::setStatus(const QString & status)
{
    m_mainwindow->setStatus(status);
}

void PioCoreImpl::import(QUrl url)
{
    ImportModule * im = getImportModule(url.scheme());
    if(im) {
        ImportDialog d(this, im, url.toString(), m_mainwindow);
        connect(&d, SIGNAL(import(ImportModule*,QList<int>,bool,bool)), m_mainwindow, SLOT(import(ImportModule*,QList<int>,bool,bool)));
        d.exec();
    }
}

void PioCoreImpl::select(MediaItemPtr item, bool select)
{
    if(item->isSelected() != select) {
        item->impl()->setSelected(select);
        if(select) {
            m_selection.append(item);
            qSort(m_selection.begin(), m_selection.end(), newer);
        }
        else
            m_selection.removeAll(item);
        emit selectionChanged(m_selection);
    }
}

void PioCoreImpl::check(MediaItemPtr item, bool check)
{
    item->impl()->setChecked(check);
    emit updated(item);
}

void PioCoreImpl::select(QSet<MediaItemPtr> selection)
{
    foreach(MediaItemPtr item, m_selection)
        item->impl()->setSelected(false);
    m_selection = selection.toList();
    foreach(MediaItemPtr item, m_selection)
        item->impl()->setSelected(true);
    qSort(m_selection.begin(), m_selection.end(), newer);
    emit selectionChanged(m_selection);
}

void PioCoreImpl::clearSelection()
{
    foreach(MediaItemPtr item, m_selection)
        item->impl()->setSelected(false);
    m_selection.clear();
    qSort(m_selection.begin(), m_selection.end(), newer);
    emit selectionChanged(m_selection);
}

void PioCoreImpl::addOverlay(QWidget *overlay)
{
    m_mainwindow->addOverlay(overlay);
}

void PioCoreImpl::removeOverlay(QWidget *overlay)
{
    m_mainwindow->removeOverlay(overlay);
}

void PioCoreImpl::addToolWidget(QWidget *widget, QString title)
{
    m_mainwindow->addToolWidget(widget, title);
}

void PioCoreImpl::removeToolWidget(QWidget *widget)
{
    m_mainwindow->removeToolWidget(widget);
}

void PioCoreImpl::selectToolWidget(QWidget *widget)
{
    m_mainwindow->selectToolWidget(widget);
}

void PioCoreImpl::moveToCollection(MediaItemPtr item, const QString &collection)
{
    QString filename = m_rootpath;
    if(!collection.isEmpty())
        filename += "/" + collection;
    QDateTime created = item->metadata().created;
    if(!created.isNull()) {
        if(collection == item->metadata().created.toString("yyyy"))
            filename += item->metadata().created.toString("/MM/dd");
        else
            filename += item->metadata().created.toString("/yyyy/MM/dd");
    }
    QFileInfo fi(item->metadata().filename);
    filename += "/" + fi.fileName();
    rename(item, filename);
    //item->impl()->move(collection, filename);
}

Database * PioCoreImpl::database()
{
    return &m_database;
}

FilterBank * PioCoreImpl::filters()
{
    return &m_filters;
}

IconCache * PioCoreImpl::cache()
{
    return &m_cache;
}

ImportModule * PioCoreImpl::getImportModule(QString scheme)
{
    if(m_importModules.contains(scheme))
        return m_importModules[scheme];
    else
        return 0;
}

bool PioCoreImpl::newer(const MediaItemPtr & a, const MediaItemPtr & b)
{
    if(a->metadata().created == b->metadata().created) {
        QFileInfo fia(a->metadata().filename), fib(b->metadata().filename);
        return fia.baseName() + "." + fia.completeSuffix() > fib.baseName() + "." + fib.completeSuffix();
    }
    return a->metadata().created > b->metadata().created;
}

void PioCoreImpl::copyMetadata(MediaItemPtr item, const MediaItemImpl::Metadata &metadata)
{
    m_database.update(item, metadata);
}

void PioCoreImpl::onAdded(MediaItemPtr item)
{
    QString filename = m_cache.engine().getThumbnailFilename(item->metadata().filename);
    QFileInfo fi(filename);
    if(!fi.exists()) {
        m_cache.enqueue(item, QSize());
    }
}

void PioCoreImpl::onItemGetInvisible(MediaItemPtr item)
{
    if(item->isSelected()) {
        select(item, false);
    }
}

void PioCoreImpl::onLoaded(MediaItemPtr item, QSize , QSharedPointer<QPixmap> pm)
{
    item->impl()->m_icon = pm;
    emit iconLoaded(item);
}
